package com.emagic.manage.common.ui.compat;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.provider.MediaStore.Images.Media;
import android.provider.MediaStore.Images.Thumbnails;
import android.util.Log;


import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;


public class MediaStoreCompat {
    public static final String TAG = MediaStoreCompat.class.getSimpleName();
    private static final String MEDIA_FILE_NAME_FORMAT = "yyyyMMdd_HHmmss";
    private static final String MEDIA_FILE_EXTENSION = ".jpg";
    private static final String MEDIA_FILE_PREFIX = "IMG_";
    private final String MEDIA_FILE_DIRECTORY;
    private Context mContext;
    private ContentObserver mObserver;
    private ArrayList<PhotoContent> mRecentlyUpdatedPhotos;

    public MediaStoreCompat(Context context, final Handler handler) {
        this.mContext = context;
        this.MEDIA_FILE_DIRECTORY = context.getPackageName();
        this.mObserver = new ContentObserver(handler) {
            public void onChange(boolean selfChange) {
                super.onChange(selfChange);
                MediaStoreCompat.this.updateLatestPhotos();
            }
        };
        this.mContext.getContentResolver().registerContentObserver(Media.EXTERNAL_CONTENT_URI, true, this.mObserver);
    }

    public static boolean hasCameraFeature(Context context) {
        PackageManager pm = context.getApplicationContext().getPackageManager();
        return pm.hasSystemFeature("android.hardware.camera");
    }

    public String invokeCameraCapture(Activity activity, int requestCode) {
        if (!hasCameraFeature(this.mContext)) {
            return null;
        } else {
            File toSave = this.getOutputFileUri();
            if (toSave == null) {
                return null;
            } else {
                Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
                intent.addCategory("android.intent.category.DEFAULT");
                intent.putExtra("output", Uri.fromFile(toSave));
                intent.putExtra("android.intent.extra.videoQuality", 1);
                intent.putExtra(Media.ORIENTATION, 0);
                activity.startActivityForResult(intent, requestCode);
                return toSave.toString();
            }
        }
    }

    public void destroy() {
        this.mContext.getContentResolver().unregisterContentObserver(this.mObserver);
    }

    public Uri getCapturedPhotoUri(Intent data, String preparedUri) {
        Uri captured = null;
        if (data != null) {
            captured = data.getData();
            if (captured == null) {
                data.getParcelableExtra("android.intent.extra.STREAM");
            }
        }

        File prepared = new File(preparedUri);
        if (captured == null || captured.equals(Uri.fromFile(prepared))) {
            captured = this.findPhotoFromRecentlyTaken(prepared);
            if (captured == null) {
                captured = this.storeImage(prepared);
                prepared.delete();
            } else {
                String realPath = getPathFromUri(this.mContext.getContentResolver(), captured);
                if (realPath != null && !prepared.equals(new File(realPath))) {
                    prepared.delete();
                }
            }
        }
        return captured;
    }

    public void cleanUp(String uri) {
        File file = new File(uri);
        if (file.exists()) {
            file.delete();
        }
    }

    public static String getPathFromUri(ContentResolver resolver, Uri contentUri) {
        Cursor cursor = null;

        String var5;
        try {
            cursor = resolver.query(contentUri, new String[]{"_data"}, null, null, null);
            if (cursor == null || !cursor.moveToFirst()) {
                return null;
            }

            int index = cursor.getColumnIndex("_data");
            var5 = cursor.getString(index);
        } finally {
            if (cursor != null) {
                cursor.close();
            }

        }

        return var5;
    }

    private Uri findPhotoFromRecentlyTaken(File file) {
        if (this.mRecentlyUpdatedPhotos == null) {
            this.updateLatestPhotos();
        }
        long fileSize = file.length();
        long taken = ExifInterfaceCompat.getExifDateTimeInMillis(file.getAbsolutePath());
        int maxPoint = 0;
        MediaStoreCompat.PhotoContent maxItem = null;

        for (PhotoContent item : this.mRecentlyUpdatedPhotos) {
            int point = 0;
            if ((long) item.size == fileSize) {
                ++point;
            }
            if (item.taken == taken) {
                ++point;
            }
            if (point > maxPoint) {
                maxPoint = point;
                maxItem = item;
            }
        }
        if (maxItem != null) {
            this.generateThumbnails(maxItem.id);
            return ContentUris.withAppendedId(Media.EXTERNAL_CONTENT_URI, maxItem.id);
        } else {
            return null;
        }
    }

    private Uri storeImage(File file) {
        try {
            int orientation = ExifInterfaceCompat.getExifOrientation(file.getAbsolutePath());

            ContentValues e = new ContentValues();
            e.put("title", file.getName());
            e.put("mime_type", "image/jpeg");
            long date = ExifInterfaceCompat.getExifDateTimeInMillis(file.getAbsolutePath());
            if (date != -1L) {
                e.put("datetaken", date);
            }

            Uri imageUri = this.mContext.getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, e);
            handleSamplingAndRotationBitmap(mContext, imageUri, file, orientation);
            this.generateThumbnails(ContentUris.parseId(imageUri));
            return imageUri;
        } catch (Exception e) {
            Log.w(TAG, "cannot insert", e);
            return null;
        }
    }

    private void updateLatestPhotos() {
        Cursor c = Media.query(this.mContext.getContentResolver(), Media.EXTERNAL_CONTENT_URI, new String[]{"_id", "datetaken", "_size"}, (String) null, (String[]) null, "date_added DESC");
        if (c != null) {
            try {
                int count = 0;
                this.mRecentlyUpdatedPhotos = new ArrayList<>();

                while (c.moveToNext()) {
                    MediaStoreCompat.PhotoContent item = new MediaStoreCompat.PhotoContent();
                    item.id = c.getLong(0);
                    item.taken = c.getLong(1);
                    item.size = c.getInt(2);
                    this.mRecentlyUpdatedPhotos.add(item);
                    ++count;
                    if (count > 5) {
                        break;
                    }
                }
            } finally {
                c.close();
            }
        }
    }

    private void generateThumbnails(long imageId) {
        try {
            Thumbnails.getThumbnail(this.mContext.getContentResolver(), imageId, 1, (Options) null);
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
    }

    @TargetApi(8)
    private File getOutputFileUri() {
        File extDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), this.MEDIA_FILE_DIRECTORY);
        if (!extDir.exists() && !extDir.mkdirs()) {
            return null;
        } else {
            String timeStamp = (new SimpleDateFormat(MEDIA_FILE_NAME_FORMAT, Locale.getDefault())).format(new Date());
            return new File(extDir.getPath() + File.separator + MEDIA_FILE_PREFIX + timeStamp + MEDIA_FILE_EXTENSION);
        }
    }

    private static class PhotoContent {
        public long id;
        public long taken;
        public int size;

        private PhotoContent() {
        }
    }

    private static final int MAX_HEIGHT = 400;
    private static final int MAX_WIDTH = 400;

    public static void handleSamplingAndRotationBitmap(Context context, Uri imageUri, File file, int orientation) throws IOException {
        // First decode with inJustDecodeBounds=true to check dimensions
        final Options options = new Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(file.getAbsolutePath(), options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        Bitmap img = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
        img = rotateImageIfRequired(orientation, img);

        OutputStream stream = context.getContentResolver().openOutputStream(imageUri);
        img.compress(Bitmap.CompressFormat.PNG, 100, stream);
        if (stream != null) {
            stream.flush();
            stream.close();
        }
    }

    /**
     * Rotate an image if required.
     */
    private static Bitmap rotateImageIfRequired(int rotation, Bitmap img) {
        // Detect rotation
        if (rotation != 0) {
            Matrix matrix = new Matrix();
            matrix.postRotate(rotation);
            Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
            img.recycle();
            return rotatedImg;
        } else {
            return img;
        }
    }

    /**
     * Calculate an inSampleSize for use in a {@link Options} object when decoding
     * bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
     * the closest inSampleSize that will result in the final decoded bitmap having a width and
     * height equal to or larger than the requested width and height. This implementation does not
     * ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
     * results in a larger bitmap which isn't as useful for caching purposes.
     *
     * @param options   An options object with out* params already populated (run through a decode*
     *                  method with inJustDecodeBounds==true
     * @param reqWidth  The requested width of the resulting bitmap
     * @param reqHeight The requested height of the resulting bitmap
     * @return The value to be used for inSampleSize
     */
    public static int calculateInSampleSize(Options options,
                                            int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            // Calculate ratios of height and width to requested height and width
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);

            // Choose the smallest ratio as inSampleSize value, this will guarantee a final image
            // with both dimensions larger than or equal to the requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;

            // This offers some additional logic in case the image has a strange
            // aspect ratio. For example, a panorama may have a much larger
            // width than height. In these cases the total pixels might still
            // end up being too large to fit comfortably in memory, so we should
            // be more aggressive with sample down the image (=larger inSampleSize).

            final float totalPixels = width * height;

            // Anything more than 2x the requested pixels we'll sample down further
            final float totalReqPixelsCap = reqWidth * reqHeight * 2;

            while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
                inSampleSize++;
            }
        }
        return inSampleSize;
    }
}
